const net = require('net');
const crypto = require('crypto');
const pong = Buffer
  .concat([
    Buffer.alloc(4), Buffer.alloc(16), Buffer.from("3")
  ]);
let remote = null;
let connections = {
  // hash:socket
};
function getPackageLength(buffer) {
  if (buffer.length < 21) {
    return 0;
  } else {
    return 21 + buffer.readInt32BE();
  }
};
const wsv = net.createServer(socket => {
  if (!remote) {
    let cache = null;
    console.log('connected remote client');
    remote = socket;
    remote.on('data', buffer => {
      if (cache) {
        let buf = Buffer.concat([cache, buffer]);
        let packageLength;
        while (packageLength = getPackageLength(buf)) {
          let tmp = buf.slice(0, packageLength);
          buf = buf.slice(packageLength);
          let hash = tmp.slice(4, 20);
          let hashString = hash.toString('hex');
          let sign = tmp.slice(20, 21).toString();
          if (sign !== "3") {
            let body = tmp.slice(21);
            if (connections[hashString]) {
              if (sign === "1") {
                connections[hashString].end();
                connections[hashString].destroy();
              } else {
                connections[hashString].write(body);
              }
            } else {
              if (sign === "0") {
                let head = Buffer.alloc(4);
                head.writeUInt32BE(0);
                let sign = Buffer.from('1');
                let data = Buffer.concat([head, hash, sign]);
                remote.write(data);
              }
            }
          } else {
            // pong
            console.log(new Date().toJSON(),'alive');
            remote.write(pong);
          }
        }
        cache = buf;
      } else {
        let buf = buffer.slice(0);
        let packageLength;
        while (packageLength = getPackageLength(buf)) {
          let tmp = buf.slice(0, packageLength);
          buf = buf.slice(packageLength);
          let hash = tmp.slice(4, 20);
          let hashString = hash.toString('hex');
          let sign = tmp.slice(20, 21).toString();
          if (sign !== "3") {
            let body = tmp.slice(21);
            if (connections[hashString]) {
              if (sign === "1") {
                connections[hashString].end();
                connections[hashString].destroy();
              } else {
                connections[hashString].write(body);
              }
            } else {
              if (sign === "0") {
                let head = Buffer.alloc(4);
                head.writeUInt32BE(0);
                let sign = Buffer.from('1');
                let data = Buffer.concat([head, hash, sign]);
                remote.write(data);
              }
            }
          } else {
            // pong
            console.log(new Date().toJSON(),'alive');
            remote.write(pong);
          }
        }
        cache = buf;
      }
    });
    remote.on('close', () => {
      console.log('disconnected from remote client');
      remote = null;
      cache = null;
      Object.keys(connections).forEach(key => {
        connections[key].end();
        connections[key].destroy();
      });
      connections = {};
    });
    remote.on('error', () => { })
  } else {
    socket.end();
    socket.destroy();
  }
});
wsv.listen(5555, '0.0.0.0', () => {
  console.log('waiting server start');
});
const psv = net.createServer(socket => {
  if (remote) {
    console.log('connected client: ',
      socket.remoteAddress + ':' + socket.remotePort);
    let hash = crypto.createHash('md5')
      .update(new Date().getTime() + ':' + socket.remoteAddress + ':' + socket.remotePort)
      .digest();
    let hashString = hash.toString('hex');
    socket.on('data', buffer => {
      if (remote) {
        let head = Buffer.alloc(4);
        head.writeUInt32BE(buffer.length);
        let sign = Buffer.from('0');
        let data = Buffer.concat([head, hash, sign, buffer]);
        remote.write(data);
      } else {
        socket.end();
        socket.destroy();
      }
    })
    socket.on('close', () => {
      console.log('disconnected from client: ',
        socket.remoteAddress + ':' + socket.remotePort);
      delete connections[hashString];
      if (remote) {
        let head = Buffer.alloc(4);
        head.writeUInt32BE(0);
        let sign = Buffer.from('1');
        let data = Buffer.concat([head, hash, sign]);
        remote.write(data);
      }
    });
    socket.on('error', () => { });
    connections[hashString] = socket;
  } else {
    socket.end();
    socket.destroy();
  }
});
psv.listen(6666, '0.0.0.0', () => {
  console.log('proxy server start');
})